package JasPerl::JSTL::Core::ImportTag;

use 5.010;
use strict;
use warnings;

use parent qw{
    JasPerl::TagExt::JspBodyTag
    JasPerl::JSTL::Core::ParamParent
};

use HTTP::Tiny;
use JasPerl::JSTL::Core::Exceptions;
use JasPerl::PageContext qw(:scopes);

use URI;

# TODO: handle character encoding
my $DEFAULT_ENCODING = "ISO-8859-1";

sub set_char_encoding {
    JasPerl::JSTL::Core::UnsupportedAttributeException->throw('import', 'charEncoding');
}

sub set_context {
    JasPerl::JSTL::Core::UnsupportedAttributeException->throw('import', 'context');
}

sub set_scope {
    $_[0]->{scope} = JasPerl::PageContext->get_scope_by_name($_[1]);
}

sub set_url {
    $_[0]->{uri} = URI->new($_[1]);
}

sub set_var {
    $_[0]->{var} = $_[1];
}

sub set_var_reader {
    $_[0]->{var_reader} = $_[1];
}

sub get_http {
    my ($self, $uri) = @_;
    my $http = HTTP::Tiny->new();
    my $response = $http->get($uri);
    JasPerl::JspTagException->throw("$uri: $response->{content}")
        if $response->{status} == 599; # HTTP::Tiny internal error
    JasPerl::JspTagException->throw("$uri: HTTP $response->{status}")
        unless $response->{success};
    return $response->{content};
}

sub get_reader {
    my $self = shift;
    my $content = $self->get_string();
    open(my $fh, '<', \$content);
    return $fh;
}

sub get_string {
    my $self = shift;
    my $uri = $self->{uri};

    # handle absolute uri via external request
    return $self->get_http($uri) if $uri->scheme;

    # TODO: checks for pc, http request/response
    my $context = $self->get_jsp_context();
    my $request = $context->get_request();
    my $response = $context->get_response();

    # TODO: handle context attribute
    # TODO: normalize url, strip session
    my $url = "$uri";

    my $dispatcher = $request->get_request_dispatcher($url)
        or JspTagException->throw("No request dispacher found for '$url'");
    my $wrapper = __PACKAGE__::ResponseWrapper->new($response);

    eval {
        $dispatcher->include($request, $wrapper);
    };

    if (my $e = JasPerl::JspException->caught()) {
        $e->rethrow();
    } elsif ($@) {
        JasPerl::JspException->throw($@);
    }

    my $status = $wrapper->get_status();
    JasPerl::JspTagException->throw("$uri: HTTP $status")
        unless $status >= 200 and $status < 300;
    return $wrapper->get_content();
}

sub release {
    my $self = shift;
    if (defined $self->{var_reader}) {
        my $context = $self->get_jsp_context();
        $context->remove_attribute($self->{var_reader}, PAGE_SCOPE);
        $self->{reader}->close() if $self->{reader};
    }
}

sub do_tag {
    my $self = shift;
    JasPerl::JSTL::Core::NullAttributeException->throw('import', 'url')
        unless $self->{uri};

    my $context = $self->get_jsp_context();

    if (defined $self->{var_reader}) {
        $self->{reader} = $self->get_reader();
        $context->set_attribute($self->{var_reader}, $self->{reader});
    }

    eval {
        $self->SUPER::do_tag();
    };

    # FIXME: wtf?
    $self->release();

    unless (defined $self->{var_reader}) {
        my $content = $self->get_string();

        if (defined $self->{var}) {
            $context->set_attribute($self->{var}, $content, $self->{scope});
        } else {
            $context->get_out()->print($content);
        }
    }
}

package # hide from PAUSE
    JasPerl::JSTL::Core::ImportTag::ResponseWrapper;

use parent qw(JasPerl::ResponseWrapper);

sub new {
    my ($class, $response) = @_;

    bless {
        content => '',
        response => $response,
        status => 200
    }, $class;
}

sub get_content {
    # TODO: handle charEncoding attribute
    return $_[0]->{content};
}

sub get_response {
    return $_[0]->{response};
}

sub get_writer {
    my $self = shift;
    # TODO: handle charEncoding attribute
    open($self->{writer}, '>', \$self->{content})
        unless $self->{writer};
    return $self->{writer};
}

sub get_status {
    return $_[0]->{status};
}

sub set_status {
    $_[0]->{status} = $_[1];
}

sub set_content_type {
    # ignore
}

sub set_locale {
    # ignore
}

DESTROY {
    $_[0]->{writer}->close() if $_[0]->{writer};
}

1;
